#ifndef HEXVIEW_H
#define HEXVIEW_H

#include <QWidget>
#include <QAbstractScrollArea>
#include <QScrollBar>
#include <QClipboard>
#include <QtGui/QApplication>

class HexView;

/*! \brief HexDataView is an abstract class to view the bytes to be displayed.
 *
 * Derive from this class to create a custom view of the bytes to be displayed. The provided paintEvent routine draws the content according to the virtual functions displayCellContent...
 */
class HexDataView : public QWidget
{
    Q_OBJECT
public:
    HexDataView(HexView *parent);
    virtual ~HexDataView() { }
    void loadBytes(uchar *buffer, uint numBytes);
    void setSelectedBytes(uint startByte, uint endByte)
    {
        selectedStartByteIndex = startByte;
        selectedEndByteIndex = endByte;
    }
    /*!
     * \return The text representation of the selected bytes
     */
    QString getSelectedText();

protected:
    //make virtual so we can override
    virtual QPoint convertPixelToCharCoord(QPoint &pixelPoint);
    virtual QPoint convertCharCoordToPixel(QPoint &charCoord);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    //void wheelEvent(QWheelEvent *event);
    virtual uint getCellWidth() const;
    virtual uint getCellHeight() const;
    virtual QString displayCellContent(uchar byte) = 0;

    virtual void paintEvent(QPaintEvent *event);
    bool hasSelection()
    {
        return selectedStartByteIndex != selectedEndByteIndex;
    }

    uchar *buffer;
    uint bufferLength;
    HexView *parent;

    QPoint cursorPoint;     //in char coord
    uint selectedStartByteIndex;     //in char coord
    uint selectedEndByteIndex;

signals:
    void bytesSelected(uint startByte, uint endByte);
protected slots:
    void bytesPerLineChanged() { }
};

class HexOffsetView : public QWidget
{
    Q_OBJECT

public:
    HexOffsetView(HexView *parent);
    void setBufferLength(uint length)
    {
        bufferLength = length;
    }
    QSize sizeHint() const;

protected:
    void paintEvent(QPaintEvent *event);
private:
    uint bufferLength;
    uint numAddressBits;
    HexView *parent;
private slots:
    void bytesPerLineChanged() { }
};

class HexByteView : public HexDataView
{
    Q_OBJECT
public:
    HexByteView(HexView *parent);
    QSize sizeHint() const;
protected:
     void paintEvent(QPaintEvent *event);
private:
    QString displayCellContent(uchar byte);
    void paintSelectedBytes();
    uint getCellWidth() const;
    uint getCellHeight() const;
};

class HexStringView : public HexDataView
{
    Q_OBJECT
public:
    HexStringView(HexView *parent);
    QSize sizeHint() const;
protected:

private:
    QString displayCellContent(uchar byte);
};


class HexView : public QAbstractScrollArea
{
    Q_OBJECT
public:
    HexView(QWidget *parent);
    ~HexView();
    void loadBytes(uchar *buffer, uint numBytes);
    uint getVisibleLines();
    void scrollContentsBy(int lineDiffX, int lineDiffY);
    QFont getFont() { return font; }
    QPoint getCharSize() { return charSize; }
    uint getBytesPerLine() { return bytesPerLine; }
    uint getHeaderHeight() { return headerRowPadding + charSize.y(); }
    uint getPadding() { return padding; }
    uint getLineOffset() { return lineOffset; }
protected:
    void paintEvent(QPaintEvent *event);
    void wheelEvent(QWheelEvent *event);
private:
    bool event(QEvent *e);

    HexOffsetView *offsetView;      //address offsets
    HexByteView *byteView;          //display all hex bytes
    HexStringView *stringView;      //string representation of bytes

    QFont font;
    uint bytesPerLine;
    uint columnPadding;
    uint headerRowPadding;
    uint padding;
    QPoint charSize;                //size of a character
    uint lineOffset;              //line offset at top border from vertical scrolling
private slots:
    void selectBytes(uint startByte, uint endByte);
    void copyBytesToClipboard();
};


#endif // HEXVIEW_H
